<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Common\Constants\JwtAuthSceneConstants;
use App\Common\Constants\SmsEventConstants;
use App\Common\Service\SmsService;
use App\CustHub\Event\ChUserLoginAfter;
use App\CustHub\Interfaces\ChUserAuthServiceInterface;
use App\CustHub\Mapper\ChUserMapper;
use App\CustHub\Model\ChUser;
use App\CustHub\Vo\ChUserServiceVo;
use App\Setting\Service\SettingConfigService;
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\Di\Annotation\Inject;
use Mine\Annotation\DependProxy;
use Mine\Event\UserLogout;
use Mine\Exception\NormalStatusException;
use Mine\Exception\UserBanException;
use Mine\Helper\MineCode;
use Mine\Helper\Str;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\SimpleCache\InvalidArgumentException;

/**
 * 用户登录服务
 */
#[DependProxy(values: [ ChUserAuthServiceInterface::class ])]
class ChUserAuthService implements ChUserAuthServiceInterface
{

    #[Inject]
    protected ChUserService $userService;

    #[Inject]
    protected ChUserMapper $mapper;

    #[Inject]
    protected SettingConfigService $configService;


    /**
     * 微信授权登录
     * @param ChUserServiceVo $userServiceVo
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function login(ChUserServiceVo $userServiceVo): array
    {
        try {
            $userinfo = $this->mapper->findByOpenid($userServiceVo->getOpenid());
            $userLoginAfter = new ChUserLoginAfter($userinfo);

            if (!empty($userinfo) && !empty($userinfo['mobile'])) {
                return $this->handleLogin($userinfo, $userLoginAfter);
            } else {
                $this->handleLoginFailure($userLoginAfter);
            }
        } catch (\Throwable $e) {
            $this->handleException($e);
        }
    }

    /**
     * 密码登录
     *
     * @param ChUserServiceVo $userServiceVo
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function passwordLogin(ChUserServiceVo $userServiceVo): array
    {
        try {
            $userinfo = $this->mapper->findByAccount($userServiceVo->getAccount());
            $userLoginAfter = new ChUserLoginAfter($userinfo);
            if (!empty($userinfo) && $this->mapper->checkPass($userServiceVo->getPassword(), $userinfo['password'])) {
                return $this->handleLogin($userinfo, $userLoginAfter);
            } else {
                $this->handleLoginFailure($userLoginAfter);
            }
        } catch (\Throwable $e) {
            $this->handleException($e);
        }
    }

    /**
     * 手机号验证码登录
     *
     * @param ChUserServiceVo $userServiceVo
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws \RedisException
     */
    public function mobileLogin(ChUserServiceVo $userServiceVo): array
    {
        $userinfo = $this->mapper->findByMobile($userServiceVo->getMobile());

        $inviteId = 0;
        if(empty($userinfo) && $this->checkInviteCodeNeed($userServiceVo->getMobile())){
            // 判断是否需要邀请码 且填写了邀请码
            if(empty($userServiceVo->getInviteCode())){
                throw new NormalStatusException('请填写邀请码');
            }

            $inviteUserInfo = $this->mapper->findByInviteCode($userServiceVo->getInviteCode());
            if(empty($inviteUserInfo)){
                throw new NormalStatusException('邀请码无效');
            }

            $inviteId = $inviteUserInfo->id;
        }

        $smsService = make(SmsService::class, [SmsEventConstants::CH_MOBILE_LOGIN]);
        if (!$smsService->verifyCode($userServiceVo->getMobile(), $userServiceVo->getCode())) {
            throw new NormalStatusException('验证码无效');
        }

        if (empty($userinfo)) {
            // 用户不存在 自动注册
            $this->userService->register($userServiceVo->getMobile(),'','','',$inviteId);
            $userinfo = $this->mapper->findByMobile($userServiceVo->getMobile());
        }

        try {
            $userLoginAfter = new ChUserLoginAfter($userinfo);
            if (!empty($userinfo)) {
                return $this->handleLogin($userinfo, $userLoginAfter);
            } else {
                $this->handleLoginFailure($userLoginAfter);
            }
        } catch (\Throwable $e) {
            $this->handleException($e);
        }
    }

    /**
     * 检查是否需要邀请码
     * @param string $mobile
     * @return bool
     */
    public function checkInviteCodeNeed(string $mobile): bool
    {
        $registerNeedInviteCode = $this->configService->getConfigByKey('register_need_invite_code')['value'] ?? "false";
        if($registerNeedInviteCode == 'true'){
           $inviteCodeNeed = !$this->mapper->checkMobileExist($mobile);
        }else{
            $inviteCodeNeed = false;
        }
        return $inviteCodeNeed;
    }


    /**
     * 处理用户的常见登录过程
     *
     * @param array $userinfo
     * @param ChUserLoginAfter $userLoginAfter
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws InvalidArgumentException
     */
    private function handleLogin(array $userinfo, ChUserLoginAfter $userLoginAfter): array
    {
        if (
            $userinfo['status'] == ChUser::ENABLE ||
            ($userinfo['status'] == ChUser::DISABLE && $userinfo['id'] == env('SUPER_ADMIN'))
        ) {
            $userLoginAfter->message = t('jwt.login_success');
            $securityKey = md5(Str::getUUID() . $userinfo['id']);
            $userLoginAfter->userinfo['security_key'] = $securityKey;
            $token = chUser()->getToken($userLoginAfter->userinfo);
            $userLoginAfter->token = $token;

            if($userinfo['parent_id'] == 0){
                //主账号初始化预置标签
                container()->get(ChTagPresetService::class)->init($userinfo['id']);
            }

            return [
                'token' => $token,
                'userInfo' => [
                    'id' => $userinfo['id'],
                    'nickname' => $userinfo['nickname'],
                    'avatar' => $userinfo['avatar'],
                    'mobile' => $userinfo['mobile'],
                    'parent_id' => $userinfo['parent_id'],
                ],
                'securityKey' => $securityKey,
            ];
        } else {
            $userLoginAfter->loginStatus = false;
            $userLoginAfter->message = t('jwt.user_ban');
            event($userLoginAfter);
            throw new UserBanException;
        }
    }

    /**
     * 注册账号
     * @param ChUserServiceVo $userServiceVo
     * @return void
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws \RedisException
     */
    public function register(ChUserServiceVo $userServiceVo): void
    {
        //验证手机验证码
        $smsService = make(SmsService::class,[SmsEventConstants::CH_REGISTER]);
        if (!$smsService->verifyCode($userServiceVo->getMobile(), $userServiceVo->getCode())) {
            throw new NormalStatusException('验证码错误');
        }

        //验证手机号是否存在
        if ($this->userService->checkUserMobileExist($userServiceVo->getMobile())) {
            throw new NormalStatusException('该手机号已被绑定');
        }
        $this->userService->register($userServiceVo->getMobile(),'','',$userServiceVo->getPassword());
    }


    /**
     * 用户退出
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function logout(): void
    {
        $user = chUser();
        event(new UserLogout($user->getUserInfo()));
        $user->getJwt()->logout();
    }


    /**
     * 处理登录失败
     *
     * @param ChUserLoginAfter $userLoginAfter
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    private function handleLoginFailure(ChUserLoginAfter $userLoginAfter): void
    {
        $userLoginAfter->loginStatus = false;
        $userLoginAfter->message = t('jwt.login_error');
        event($userLoginAfter);
        throw new NormalStatusException;
    }

    /**
     * 处理登录过程中的异常
     *
     * @param \Throwable $e
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    private function handleException(\Throwable $e): void
    {
        if ($e instanceof ModelNotFoundException) {
            throw new NormalStatusException(t('jwt.login_error'), MineCode::NO_USER);
        }
        if ($e instanceof NormalStatusException) {
            throw new NormalStatusException(t('jwt.login_error'), MineCode::NO_USER);
        }
        if ($e instanceof UserBanException) {
            throw new NormalStatusException(t('jwt.user_ban'), MineCode::USER_BAN);
        }
        throw new NormalStatusException(t('jwt.unknown_error'));
    }
}